package jxtend.processor.eclipse.visitor.handler.impl;

import jxtend.annotation.keyword.workinprogress.nullable;
import jxtend.processor.eclipse.utils.EclipseProcessorUtils;
import jxtend.processor.eclipse.visitor.handler.ASTNodeHandlerVisitorContext;
import jxtend.processor.eclipse.visitor.handler.AbstractASTNodeVisitorHandler;

import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FalseLiteral;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.StringLiteral;
import org.eclipse.jdt.internal.compiler.ast.TrueLiteral;
import org.eclipse.jdt.internal.compiler.lookup.Scope;

@SuppressWarnings("restriction")
public class ImplicitNotNullHandler extends AbstractASTNodeVisitorHandler {

	@Override
	public boolean endVisitBeforeResolve(ASTNodeHandlerVisitorContext context, ASTNode node, Scope scope) {
		ASTNode parentNode = context.getParentNode();

		if (!(((node instanceof Argument) && ((parentNode instanceof MethodDeclaration) 
						|| (parentNode instanceof ConstructorDeclaration)))
				|| (node instanceof MethodDeclaration))) {
			return true;
		}

		if (node instanceof Argument) {
			Argument argument = (Argument) node;

			Annotation nullableAnnotation = EclipseProcessorUtils.findAnnotation(argument.annotations, nullable.class);

			if (nullableAnnotation != null) {
				return true;
			}

			MessageSend messageSend = new MessageSend();
			long[] positions = new long[JxtendTypeConstants.QUALIFIED_JXTEND_INTERNAL_UTILS.length];
			messageSend.receiver = new QualifiedNameReference(JxtendTypeConstants.QUALIFIED_JXTEND_INTERNAL_UTILS,
					positions, argument.sourceStart, argument.sourceEnd);
			messageSend.selector = "checkArgumentNotNull".toCharArray();
			messageSend.arguments = new Expression[] {
					new StringLiteral(argument.name, argument.sourceStart, argument.sourceEnd, 0),
					new SingleNameReference(
							argument.name, EclipseProcessorUtils.encodePos(argument.sourceStart, argument.sourceEnd)),
					(parentNode instanceof ConstructorDeclaration)
						? new TrueLiteral(argument.sourceStart, argument.sourceEnd)
						: new FalseLiteral(argument.sourceStart, argument.sourceEnd) };

			context.prependStatement((AbstractMethodDeclaration) parentNode, messageSend);
		} else {
			assert node instanceof MethodDeclaration;

		}

		return true;
	}
}
